package com.amiu.shiro;

import java.util.concurrent.atomic.AtomicInteger;

import net.sf.ehcache.Ehcache;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;

import com.amiu.util.DateUtil;
/**
 * RetryLimitHashedCredentials 重试限制哈希认证
 * 功能1：可以设置密码解密规则，匹配加密的密码
 * 功能2：可以设置短时间内登陆密码连续错误次数，短时间内超过次数则锁定，设置时间内无法登陆
 * @author amiu
 * @version createtime：2017-9-19 下午12:08:16
 */
public class RetryLimitHashedCredentials extends HashedCredentialsMatcher{
	private int allowedErrorCount;//允许错误次数
	private int lockSecond;//锁定时间(秒) 
	private String retryLimitCacheName;//cache名称
	private final static String DEFAULT_CHACHE_NAME = "passwordRetryCache";//默认cache名称
	
	private CacheManager cacheManager;//shiro缓存管理器
	//shiro cache,密码错误重复次数的缓存
	private org.apache.shiro.cache.Cache<String, AtomicInteger> passwordRetryCache;
	
	public RetryLimitHashedCredentials(CacheManager cacheManager) {
		this.cacheManager = cacheManager;
		this.retryLimitCacheName = DEFAULT_CHACHE_NAME;
	}
	
	public RetryLimitHashedCredentials(CacheManager cacheManager,String cacheName) {
		this.cacheManager = cacheManager;
		this.retryLimitCacheName = cacheName;
	}

	public int getLockSecond() {
		return lockSecond;
	}
	/**
	 * 设置锁定时间(秒) 
	 * @param lockSecond 锁定时间(秒)
	 */
	public void setLockSecond(int lockSecond) {
		this.lockSecond = lockSecond;
	}

	public int getAllowedErrorCount() {
		return allowedErrorCount;
	}
	/**
	 * 设置允许错误次数
	 * @param allowedErrorCount 允许错误次数
	 */
	public void setAllowedErrorCount(int allowedErrorCount) {
		this.allowedErrorCount = allowedErrorCount;
	}

//	public void setCacheManager(CacheManager cacheManager) {
//		this.cacheManager = cacheManager;
//	}

	public String getRetryLimitCacheName() {
		return retryLimitCacheName;
	}
	/**
	 * 设置 cache名称
	 * @param retryLimitCacheName
	 */
	public void setRetryLimitCacheName(String retryLimitCacheName) {
		this.retryLimitCacheName = retryLimitCacheName;
	}

	private void initializeCache(){
		passwordRetryCache = cacheManager.getCache(retryLimitCacheName);
	}
	
	//重写认证方法，添加我们的超次锁定逻辑
	@Override
	public boolean doCredentialsMatch(AuthenticationToken token,
			AuthenticationInfo info) {
		if(passwordRetryCache == null) initializeCache();
		
		@SuppressWarnings("unused")
		Object o = token.getCredentials();
		String username = (String)token.getPrincipal();
		//AtomicInteger简介：是一个提供原子操作的Integer类，通过线程安全的方式操作加减
		//获取对应cache的值
		AtomicInteger retryCount = passwordRetryCache.get(username);
		
		if(retryCount == null){
			//如果为空，说明还未存入cache，先new一个，放入cache
			retryCount = new AtomicInteger(0);
			passwordRetryCache.put(username, retryCount);
		}
		if(retryCount.incrementAndGet() > allowedErrorCount){
			//自增次数且如果输入密码次数大于N次，则抛异常
			throw new ExcessiveAttemptsException(String.format(
					"用户名[%s]密码连续输入错误超过 %s 次，锁定 %s 分钟！",
					username,allowedErrorCount,
					DateUtil.secondToMin(lockSecond)));
		}else{
			//否则更新cache
			passwordRetryCache.put(username, retryCount);
		}
		boolean matches = super.doCredentialsMatch(token, info);//是否登录成功
		if(matches){
			//登录成功则移除缓存
			passwordRetryCache.remove(username);
		}
		return matches;
	}

	
	
	
}
